import 'dart:math';

import 'package:eq/native/constants.dart';
import 'package:eq/src/core.dart';
import 'package:eq/src/interpreter.dart';
import 'package:eq/src/utils.dart';

void addMathFunctions(Module module) {
  _addMathConstantValue();

  addNativeFunction('min', _min, module);
  addNativeFunction('max', _max, module);
  addNativeFunction('getModulo', _getModulo, module);
  addNativeFunction('randomInt', _randomInt, module);
  addNativeFunction('randomDouble', _randomDouble, module);
  addNativeFunction('randomBool', _randomBool, module);
  addNativeFunction('pow', _pow, module);
  addNativeFunction('atan2', _atan2, module);
  addNativeFunction('sin', _sin, module);
  addNativeFunction('cos', _cos, module);
  addNativeFunction('tan', _tan, module);
  addNativeFunction('acos', _acos, module);
  addNativeFunction('asin', _asin, module);
  addNativeFunction('atan', _atan, module);
  addNativeFunction('sqrt', _sqrt, module);
  addNativeFunction('exp', _exp, module);
  addNativeFunction('log', _log, module);
}

void _addMathConstantValue() {
  Interpreter.getInstance()
      .module[eq_math]!
      .variable
      .add(Variable('e', Value(ValueType.DOUBLE_VALUE, e), isFixed: true));
  Interpreter.getInstance().module[eq_math]!.variable.add(
      Variable('ln10', Value(ValueType.DOUBLE_VALUE, ln10), isFixed: true));
  Interpreter.getInstance()
      .module[eq_math]!
      .variable
      .add(Variable('ln2', Value(ValueType.DOUBLE_VALUE, ln2), isFixed: true));
  Interpreter.getInstance().module[eq_math]!.variable.add(
      Variable('log2e', Value(ValueType.DOUBLE_VALUE, log2e), isFixed: true));
  Interpreter.getInstance().module[eq_math]!.variable.add(
      Variable('log10e', Value(ValueType.DOUBLE_VALUE, log10e), isFixed: true));
  Interpreter.getInstance()
      .module[eq_math]!
      .variable
      .add(Variable('pi', Value(ValueType.DOUBLE_VALUE, pi), isFixed: true));
  Interpreter.getInstance().module[eq_math]!.variable.add(Variable(
      'sqrt1_2', Value(ValueType.DOUBLE_VALUE, sqrt1_2),
      isFixed: true));
  Interpreter.getInstance().module[eq_math]!.variable.add(
      Variable('sqrt2', Value(ValueType.DOUBLE_VALUE, sqrt2), isFixed: true));
}

Value _min(List<Value> list) {
  Value? value;
  if (list.isNotEmpty) {
    for (int i = 0; i < list.length; i++) {
      if (list[i].type == ValueType.INT_VALUE ||
          list[i].type == ValueType.DOUBLE_VALUE) {
        if (value != null) {
          if (value.value > list[i].value) value = list[i];
        } else {
          value = list[i];
        }
      }
    }
  }

  return value ?? Value(ValueType.NULL_VALUE, null);
}

Value _max(List<Value> list) {
  Value? value;
  if (list.isNotEmpty) {
    for (int i = 0; i < list.length; i++) {
      if (list[i].type == ValueType.INT_VALUE ||
          list[i].type == ValueType.DOUBLE_VALUE) {
        if (value != null) {
          if (value.value < list[i].value) value = list[i];
        } else {
          value = list[i];
        }
      }
    }
  }

  return value ?? Value(ValueType.NULL_VALUE, null);
}

Value _getModulo(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(element.type, element.value.abs()));
    } else if (element.type == ValueType.COMPLEX_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, element.value.getModulo()));
    } else {
      t1.add(element);
    }
  }

  if (t1.length == 1) {
    return t1.first;
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _randomInt(List<Value> list) {
  if (list.isNotEmpty) {
    if ((list[0].type == ValueType.INT_VALUE)) {
      List t1 = List.empty(growable: true);
      Random random = Random();
      if (list.length >= 2 && list[1].type == ValueType.INT_VALUE) {
        for (int i = 0; i < list[1].value; i++) {
          t1.add(Value(ValueType.INT_VALUE, random.nextInt(list[0].value)));
        }
      } else {
        t1.add(Value(ValueType.INT_VALUE, random.nextInt(list[0].value)));
      }
      if (t1.length == 1) {
        return Value(ValueType.INT_VALUE, t1.first);
      }
      return Value(ValueType.ARRAY_VALUE, t1);
    }
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _randomDouble(List<Value> list) {
  List t1 = List.empty(growable: true);
  Random random = Random();
  if (list.isNotEmpty && list[0].type == ValueType.INT_VALUE) {
    for (int i = 0; i < list[0].value; i++) {
      t1.add(Value(ValueType.DOUBLE_VALUE, random.nextDouble()));
    }
  } else {
    t1.add(Value(ValueType.DOUBLE_VALUE, random.nextDouble()));
  }
  if (t1.length == 1) {
    return Value(ValueType.DOUBLE_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _randomBool(List<Value> list) {
  List t1 = List.empty(growable: true);
  Random random = Random();
  if (list.isNotEmpty && list[0].type == ValueType.INT_VALUE) {
    for (int i = 0; i < list[0].value; i++) {
      t1.add(Value(ValueType.BOOLEAN_VALUE, random.nextBool()));
    }
  } else {
    t1.add(Value(ValueType.BOOLEAN_VALUE, random.nextBool()));
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _atan2(List<Value> list) {
  if (list.length == 2) {
    if ((list[0].type == ValueType.INT_VALUE ||
            list[0].type == ValueType.DOUBLE_VALUE) &&
        (list[1].type == ValueType.INT_VALUE ||
            list[1].type == ValueType.DOUBLE_VALUE)) {
      return Value(ValueType.DOUBLE_VALUE, atan2(list[0].value, list[1].value));
    }
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _pow(List<Value> list) {
  if (list.length == 2) {
    if ((list[0].type == ValueType.INT_VALUE ||
            list[0].type == ValueType.DOUBLE_VALUE) &&
        (list[1].type == ValueType.INT_VALUE ||
            list[1].type == ValueType.DOUBLE_VALUE)) {
      return Value(ValueType.DOUBLE_VALUE, pow(list[0].value, list[1].value));
    }
  }
  return Value(ValueType.NULL_VALUE, null);
}

Value _sin(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, sin(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _cos(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, cos(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _tan(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, tan(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _acos(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, acos(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _asin(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, asin(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _atan(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, atan(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _sqrt(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, sqrt(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _exp(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, exp(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}

Value _log(List<Value> list) {
  List<Value> t1 = List.empty(growable: true);
  for (var element in list) {
    if (element.type == ValueType.INT_VALUE ||
        element.type == ValueType.DOUBLE_VALUE) {
      t1.add(Value(ValueType.DOUBLE_VALUE, log(element.value)));
    }
  }
  if (t1.length == 1) {
    return Value(ValueType.BOOLEAN_VALUE, t1.first);
  }
  return Value(ValueType.ARRAY_VALUE, t1);
}
